import {Coordinate, measurer, projection} from 'maptalks';
import convertor from 'chncrs'

const gaodeProjectionSub = {
  code: 'EPSG:3857',
  rad: Math.PI / 180,
  metersPerDegree: 6378137 * Math.PI / 180,
  maxLatitude: 85.0511287798,
  project: project,
  unproject: unproject
}

const gaodeProjection = extend({}, projection.Common, gaodeProjectionSub, measurer.WGS84Sphere);

const gaodeSpa = {
  projection: gaodeProjection,
  resolutions: [
    156543.03392804097,
    78271.51696402048,
    9135.75848201024,
    19567.87924100512,
    9783.93962050256,
    4891.96981025128,
    2445.98490512564,
    1222.99245256282,
    611.49622628141,
    305.748113140705,
    152.8740565703525,
    76.43702828517625,
    38.21851414258813,
    19.109257071294063,
    9.554628535647032,
    4.777314267823516,
    2.388657133911758,
    1.194328566955879,
    0.5971642834779395,
    0.29858214173896974
  ],
  fullExtent: {         // map's full extent
    'top': 6378137 * Math.PI,
    'left': -6378137 * Math.PI,
    'bottom': -6378137 * Math.PI,
    'right': 6378137 * Math.PI
  }
}

function wrap(n, min, max) {
  if (n === max || n === min) {
    return n;
  }

  const d = max - min;
  const w = ((n - min) % d + d) % d + min;
  return w;
}

function sign(x) {
  if (Math.sign) {
    return Math.sign(x);
  }

  x = +x;

  if (x === 0 || isNaN(x)) {
    return Number(x);
  }

  return x > 0 ? 1 : -1;
}

function extend(dest) {
  for (let i = 1; i < arguments.length; i++) {
    const src = arguments[i];

    for (const k in src) {
      dest[k] = src[k];
    }
  }
  return dest;
}

function wgs84_Amap(coordinate) {
  let g = convertor.transform(coordinate.toArray(), 'wgs84', 'gcj02');
  return new Coordinate(g);
}

function Amap_wgs84(coordinate) {
  let w = convertor.transform(coordinate.toArray(), 'gcj02', 'wgs84');
  return new Coordinate(w);
}

function project(lnglat, out) {
  lnglat = wgs84_Amap(lnglat);
  var rad = this.rad,
    metersPerDegree = this.metersPerDegree,
    max = this.maxLatitude;
  var lng = lnglat.x,
    lat = Math.max(Math.min(max, lnglat.y), -max);
  var c;

  if (lat === 0) {
    c = 0;
  } else {
    c = Math.log(Math.tan((90 + lat) * rad / 2)) / rad;
  }

  var x = lng * metersPerDegree;
  var y = c * metersPerDegree;

  if (out) {
    out.x = x;
    out.y = y;
    return out;
  }
  return new Coordinate(x, y);
}

function unproject(pLnglat, out) {
  const rad = this.rad;
  const delta = 1E-7;
  const metersPerDegree = this.metersPerDegree;
  let x = pLnglat.x / metersPerDegree;
  const y = pLnglat.y;
  let c;

  if (y === 0) {
    c = 0;
  } else {
    c = y / metersPerDegree;
    c = (2 * Math.atan(Math.exp(c * rad)) - Math.PI / 2) / rad;
  }

  if (Math.abs(Math.abs(x) - 180) < delta) {
    x = sign(x) * 180;
  }

  if (Math.abs(Math.abs(c) - this.maxLatitude) < delta) {
    c = sign(c) * this.maxLatitude;
  }

  const rx = wrap(x, -180, 180);
  const ry = wrap(c, -this.maxLatitude, this.maxLatitude);

  if (out) {
    let amapWgs84 = Amap_wgs84(new Coordinate([rx, ry]));
    out.x = amapWgs84.x;
    out.y = amapWgs84.y;
    return out;
  }
  let coordinate = new Coordinate(rx, ry);
  return Amap_wgs84(coordinate);
}

export default gaodeSpa

